// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.


/*
   

    File Replication Sample
    File Replication Client Utility
    
    FILE: FileRep.cpp
    
    USAGE: 
        FileRep [server name] [remote file] [local file] <options...>

    OPTIONS:
        -n network_address
        -p protocol_sequence
        -e endpoint
        -d print messages
                         
    PURPOSE: Client utility for the file replication application
    
    FUNCTIONS: main() - binds to replication server and requests
        file replication.
    
    COMMENTS:

*/

#include "common.h"

#include <process.h>
#include <rpc.h>
#include <Ntdsapi.h>

#include "Service.h"

// header file generated by MIDL compiler
#include "FileRepClient.h"

/*
    FUNCTION: PrintUsage()

    PURPOSE: Prints help information.

    PARAMETERS:
        ProgName - name of the executable

    RETURN VALUE:
        none

    COMMENTS:
*/
void PrintUsage(TCHAR *ProgName) {
    _tprintf_s(TEXT("%s - File Replication Client Utility\n\n"), ProgName);
    _tprintf_s(TEXT("Usage: %s [server name] [remote file] [local file] <options...> \n\n"), ProgName);
    _tprintf_s(TEXT("Options:\n"));
    _tprintf_s(TEXT(" -p protocol_sequence\n"));
    _tprintf_s(TEXT(" -n network_address\n"));
    _tprintf_s(TEXT(" -e endpoint\n"));
    _tprintf_s(TEXT(" -d print messages\n"));
}

/*
    FUNCTION: main()

    PURPOSE: Main funciton for the utility.  It binds to the local server
        and requests file replication.

    PARAMETERS:
        dwArgc - number of command line arguments
        lpszArgv - array of command line arguments

    RETURN VALUE:
        none

    COMMENTS:

*/
INT _cdecl main(int argc, char **argv) {

    INT i;
    RPC_STATUS rpcstatus;

    // Controlls printing out of messages.
    BOOL bDebug = FALSE;

    // Explicit binding handle to the client system service.
    handle_t hFileRepClient = NULL;

    // These are the default values for extablishing connections.
    // Default connection is to a local server over local rpc.
    RPC_STR pszProtocolSequence = (RPC_STR)TEXT("ncalrpc");
    RPC_STR pszNetworkAddress = NULL;

    // An empty endpoint string is used, since we are going to
    // connect to the endpoint dynamically generated by the
    // RPC run-time library.  Server calls RpcServerUseProtseq to
    // obtain a binding hadnle and a dynamic endpoint.
    RPC_STR pszEndpoint = (RPC_STR)TEXT("");

    RPC_STR pszUuid = NULL;
    RPC_STR pszOptions = NULL;
    RPC_STR pszStringBinding = NULL;

    RPC_STR pszServerPrincipalName = NULL;

    LPTSTR ServerName = NULL;
    LPTSTR RemoteFileName = NULL;
    LPTSTR LocalFileName = NULL;

    INT nNumArgs;

#ifndef NO_SEC
    // Quality of service structure to ensure authentication.
    RPC_SECURITY_QOS SecurityQOS;

    // Used in generating the principal name for the local system service.
    TCHAR Name[128];
    DWORD cbName = sizeof(Name);

    TCHAR DomainName[128];
    DWORD cbDomainName = sizeof(DomainName);
    SID_NAME_USE Use;

    SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY;
    BYTE sidBuffer[100];
    PSID pSID = (PSID)&sidBuffer;
#endif

    // Get a common handle on the command line arguments for both 
    // UNICODE and ASCII
#ifdef _UNICODE
    LPWSTR *szArgList = CommandLineToArgvW(GetCommandLine(), &nNumArgs);
    if (NULL == szArgList) {
        _tprintf_s(TEXT("FileRep main: CommandLineToArgW failed"));
        exit(EXIT_FAILURE);
    }
#else
    char **szArgList = argv;
    nNumArgs = argc;
#endif

    // Check that the correct number of arguments is given.
    if(nNumArgs<4){
        PrintUsage(szArgList[0]);
        exit(EXIT_FAILURE);
    }

    // Extract the required arguments.
    ServerName = szArgList[1];
    RemoteFileName = szArgList[2];
    LocalFileName = szArgList[3];

    // Allow the user to override settings with command line switches.
    for (i = 4; i < nNumArgs; i++) {
        // Well-formed argument switches start with '/' or '-' and are
        // two characters long.
        if (((*szArgList[i] == TEXT('-')) || (*szArgList[i] == TEXT('/'))) && _tcsclen(szArgList[i]) == 2) {

            switch (_totlower(*(szArgList[i]+1))) {

                case TEXT('p'):

                    // Override protocol sequence.

                    // Check that the next argument exists and is not a
                    // switch.
                    if(i+1 < nNumArgs && *szArgList[i+1] != TEXT('-') && *szArgList[i+1] != TEXT('/')) {
                        pszProtocolSequence = (RPC_STR)szArgList[++i];
                    }
                    // Otherwise we have an empty protocol sequence.
                    else {
                        // We do not allow an empty protocol sequence.
                        _tprintf_s(TEXT("Protocol sequence can't be \"\".\n"));
                        exit(EXIT_FAILURE);
                    }
                    
                    break;
                    
                case TEXT('n'):

                    // Override the network address.
                    if(i+1 < nNumArgs && *szArgList[i+1] != TEXT('-') && *szArgList[i+1] != TEXT('/')) {
                        pszNetworkAddress = (RPC_STR)szArgList[++i];
                    }
                    else {
                        pszNetworkAddress = (RPC_STR)TEXT("");
                    }
                    
                    break;
                    
                case TEXT('e'):

                    // Override the endpoint.
                    if(i+1 < nNumArgs && *szArgList[i+1] != TEXT('-') && *szArgList[i+1] != TEXT('/')) {
                        pszEndpoint = (RPC_STR)szArgList[++i];
                    }
                    else {
                        pszEndpoint = (RPC_STR)TEXT("");
                    }
                    
                    break;
                    
                case TEXT('d'):
                    // Turn on debugging.
                    bDebug = TRUE;
                    break; 
                    
                case TEXT('h'):
                case TEXT('?'):
                default:
                    PrintUsage(szArgList[0]);
                    exit(EXIT_SUCCESS);
            }
        }
        else {
            PrintUsage(szArgList[0]);
            exit(EXIT_FAILURE);
        }
    }

    // Concatenate the elements of
    // the string binding into the proper sequence.
    rpcstatus = RpcStringBindingCompose(pszUuid,
                                        pszProtocolSequence,
                                        pszNetworkAddress,
                                        pszEndpoint,
                                        pszOptions,
                                        &pszStringBinding);
    if(bDebug){
        _tprintf_s(TEXT("RpcStringBindingCompose returned 0x%x\n"), rpcstatus);
        _tprintf_s(TEXT("pszStringBinding = %s\n"), pszStringBinding);
    }
    RPC_EEINFO_EXIT_IF_FAIL(rpcstatus, TEXT("RpcStringBindingCompose"));

    // Set the binding handle that will be used to bind to the server.
    rpcstatus = RpcBindingFromStringBinding(pszStringBinding, &hFileRepClient);
    if(bDebug) _tprintf_s(TEXT("RpcBindingFromStringBinding returned 0x%x\n"), rpcstatus);
    RPC_EEINFO_EXIT_IF_FAIL(rpcstatus, TEXT("RpcBindingFromStringBinding"));

    // Free the binding string.
    rpcstatus = RpcStringFree(&pszStringBinding);
    ASSERT(rpcstatus == RPC_S_OK);

#ifndef NO_SEC
    // Specify quality of service parameters.
    SecurityQOS.Version = RPC_C_SECURITY_QOS_VERSION;
    SecurityQOS.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
    // Dynamic identity tracking is more efficient for a single LRPC call.
    SecurityQOS.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
    // The client system service needs to impersonate the security context
    // of the client utility on the remote systems where it may replicate
    // files from.
    // We need to impersonate with level delegate, since the server system
    // service will need to impersonate the user further.
    SecurityQOS.ImpersonationType = RPC_C_IMP_LEVEL_DELEGATE;

    // Generate the principal name for the local system service.
    // We can't just use the a string constant, because then
    // we rely on a particular English-language representation of the
    // principal.

    if (AllocateAndInitializeSid(&SIDAuth, 1,
                                 SECURITY_NETWORK_SERVICE_RID,
                                 0, 0, 0, 0, 0, 0, 0,
                                 &pSID) == 0) {
        printf_s("AllocateAndInitializeSid failed\n");
        exit(EXIT_FAILURE);
    }

    if (LookupAccountSid(NULL, // name of local or remote computer
                         pSID, // security identifier
                         Name, // account name buffer
                         &cbName, // size of account name buffer
                         DomainName, // domain name
                         &cbDomainName, // size of domain name buffer
                         &Use) == 0) { // SID type
        printf_s("LookupAccountSid failed\n");
        exit(EXIT_FAILURE);
    }

    if(bDebug) _tprintf_s(TEXT("Server \"magic name\" is %s\n"), Name);

    pszServerPrincipalName = (RPC_STR)Name;

    // Set authentication and authorisation information for
    // the binding handle.
    // By speciying NULL for the 5th parameter we use the security login
    // context for the current address space.
    // The security level is "PRIVACY" since it is the only level
    // provided by LRPC.
    // We are assured of talking to a local service running with 
    // system privileges.
    rpcstatus = RpcBindingSetAuthInfoEx(hFileRepClient, pszServerPrincipalName, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_WINNT, NULL, NULL, &SecurityQOS);
    RPC_EEINFO_EXIT_IF_FAIL(rpcstatus, TEXT("RpcBindingSetAuthInfo"));

#endif

    // Make the RPC to request file replication.
    RpcTryExcept {
        if(bDebug) _tprintf_s(TEXT("Calling the remote procedure RequestFile(hFileRepClient, %s, %s, %s)\n"), ServerName, RemoteFileName, LocalFileName);

        RequestFile(hFileRepClient, ServerName, RemoteFileName, LocalFileName);
        
        if(bDebug) _tprintf_s(TEXT("RequestFile() finished\n"));
    }
    // Return "non-fatal" errors.  Catching fatal errors
    // makes it harder to debug.
    RpcExcept ( ( (RpcExceptionCode() != STATUS_ACCESS_VIOLATION) &&
                  (RpcExceptionCode() != STATUS_DATATYPE_MISALIGNMENT) &&
                  (RpcExceptionCode() != STATUS_PRIVILEGED_INSTRUCTION) &&
                  (RpcExceptionCode() != STATUS_ILLEGAL_INSTRUCTION) &&
                  (RpcExceptionCode() != STATUS_BREAKPOINT))
                  ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
     
	    PrintProcFailureEEInfo(TEXT("RequestFile"), RpcExceptionCode()); 
        exit(EXIT_FAILURE);
    }
    RpcEndExcept;

    // Free the binding.
    rpcstatus = RpcBindingFree((VOID **)&hFileRepClient);
    ASSERT(rpcstatus == RPC_S_OK);
    
    if(bDebug) _tprintf_s(TEXT("Success!\n"));
    
    return(EXIT_SUCCESS);

} // end main()

/*
    MIDL allocate() and free()
*/

VOID __RPC_FAR * __RPC_API midl_user_allocate(size_t len) {
    return(AutoHeapAlloc(len));
}

VOID __RPC_API midl_user_free(VOID __RPC_FAR * ptr) {
    if(ptr != NULL) {
        AutoHeapFree(ptr);
    }
}

// end FileRep.cpp
